#include "Ogre7zFile.h"

#include "Ogre7zCommon.h"

#include <OgreException.h>

namespace Ogre
{

//-------------------------------------------------------------------------------------------------------------------------------------------------
SevenZipFile::SevenZipFile( const String& name )
: mName(name)
, mLoaded(false)
, mBlockIndex(0xFFFFFFFF)
, mOutBuffer(0)
, mOutBufferSize(0)
{
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
SevenZipFile::~SevenZipFile()
{
	// destroy internal data
	unload();
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipFile::load()
{
	// Is loaded?
	if( mLoaded )
		return;

	// Open archive file
	if( InFile_Open( &mArchiveStream.file, mName.c_str() ) )
	{
		OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
			"Unable to open 7zip archive '" + mName + "'",
			"SevenZipFile::load" );
		return;
	}

	// Prepare 7zip tables
	FileInStream_CreateVTable( &mArchiveStream );
	LookToRead_CreateVTable( &mLookStream, 0 );
	
	mLookStream.realStream = &mArchiveStream.s;
	LookToRead_Init( &mLookStream );

	// Open archive to extract
	SzArEx_Init( &mDb );
	SRes res = SzArEx_Open( &mDb, &mLookStream.s,
		SevenZipUtil::getISzAlloc(), SevenZipUtil::getISzAlloc() );

	if( res != SZ_OK )
	{
		// Free extraction data
		SzArEx_Free( (CSzArEx*)&mDb, SevenZipUtil::getISzAlloc() );

		// Close archive
		File_Close( &mArchiveStream.file );

		OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
			"Unable to open 7zip archive '" + mName + 
			"', error: " + SevenZipUtil::getErrorString( res ),
			"SevenZipFile::load" );
	}

	mLoaded = true;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipFile::unload()
{
	// Is loaded?
	if( !mLoaded )
		return;

	// Free out buffer
	if( mOutBuffer )
	{
		IAlloc_Free( SevenZipUtil::getISzAlloc(), mOutBuffer );
		mOutBuffer = 0;
	}

	// Free extraction data
	SzArEx_Free( (CSzArEx*)&mDb, SevenZipUtil::getISzAlloc() );

	// Close archive
	File_Close( &mArchiveStream.file );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
bool SevenZipFile::getIsLoaded() const
{
	return mLoaded;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
const CSzArEx& SevenZipFile::getExtractData() const
{
	return mDb;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
size_t SevenZipFile::getNumFiles() const
{
	return (size_t)mDb.db.NumFiles;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
const CSzFileItem* SevenZipFile::getFileItem( size_t index ) const
{
	return mDb.db.Files + index;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
String SevenZipFile::getFileName( size_t index ) const
{
	String ret;
	getFileName( index, ret );

	return ret;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
void SevenZipFile::getFileName( size_t index, String& name ) const
{
	// Retrieve name length
	size_t len = SzArEx_GetFileNameUtf16( &mDb, index, NULL );

	// Alloc file name buffeer
	UInt16* tmpFileName = (UInt16*) OGRE_MALLOC_SIMD( len * sizeof(UInt16), MEMCATEGORY_GENERAL );
	// Retrieve file name
	SzArEx_GetFileNameUtf16( &mDb, index, tmpFileName );

	// Convert file name to utf8
	SevenZipUtil::Utf16ToUtf8( name, (const short*)tmpFileName, len - 1 );

	// Destroy buffer
	OGRE_FREE_SIMD( tmpFileName, MEMCATEGORY_GENERAL );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
time_t SevenZipFile::getModifiedTime( size_t index ) const
{
	return *(time_t*)&((mDb.db.Files + index)->MTime);
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
size_t SevenZipFile::getFileSize( size_t index ) const
{
	return (size_t)(mDb.db.Files + index)->Size;
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
bool SevenZipFile::getIsDirectory( size_t index ) const
{
	return ((mDb.db.Files + index)->IsDir != 0);
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
size_t SevenZipFile::getFileIndex( const String& name ) const
{
	// Itarate all files
	for( size_t index = 0; index < (size_t)mDb.db.NumFiles; index++ )
	{
		String filename;
		getFileName( index, filename );

		if( filename == name )
			return index;
	}

	return size_t(-1);
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
SRes SevenZipFile::extract( size_t index, size_t& offset, size_t& outSizeProcessed )
{
	return SzArEx_Extract( &mDb, &mLookStream.s, index,
		&mBlockIndex, &mOutBuffer, &mOutBufferSize, &offset, &outSizeProcessed,
		SevenZipUtil::getISzAlloc(), SevenZipUtil::getISzAlloc() );
}

//-------------------------------------------------------------------------------------------------------------------------------------------------
const Byte* SevenZipFile::getOutBuffer() const
{
	return mOutBuffer;
}

} // end of namespace Ogre
